﻿
using System.Runtime.CompilerServices;
using Unity.Mathematics;

namespace GameBasic
{
    public static class ExtInt
    {
        #region BitCount
        public static readonly byte[] BitCountTable =
        {
            0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
            1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
            1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
            2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
            1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
            2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
            2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
            3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
            1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
            2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
            2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
            3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
            2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
            3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
            3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
            4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
         };

        public static int BitCount(this int number)
        {
            return BitCountTable[number & 0xff] +
            BitCountTable[(number >> 8) & 0xff] +
            BitCountTable[(number >> 16) & 0xff] +
            BitCountTable[(number >> 24) & 0xff];
        }

        static int BitCount2(this int number)
        {
            if (number == 0)
                return 0;

            int result = 0;
            for (int i = 0; i < 32; i++)
            {
                result += (number >> i) & 1;
            }
            return result;
        }

        static int BitCount2(this long number)
        {
            if (number == 0)
                return 0;

            long result = 0;
            for (int i = 0; i < 64; i++)
            {
                result += (number >> i) & 1;
            }
            return (int)result;
        }

        public static int BitCount(this long number)
        {
            return BitCountTable[number & 0xff] +
            BitCountTable[(number >> 8) & 0xff] +
            BitCountTable[(number >> 16) & 0xff] +
            BitCountTable[(number >> 24) & 0xff] +
            BitCountTable[(number >> 32) & 0xff] +
            BitCountTable[(number >> 40) & 0xff] +
            BitCountTable[(number >> 48) & 0xff] +
            BitCountTable[(number >> 56) & 0xff];
        }

        public static int BitCount(this ulong number)
        {
            return BitCountTable[number & 0xff] +
            BitCountTable[(number >> 8) & 0xff] +
            BitCountTable[(number >> 16) & 0xff] +
            BitCountTable[(number >> 24) & 0xff] +
            BitCountTable[(number >> 32) & 0xff] +
            BitCountTable[(number >> 40) & 0xff] +
            BitCountTable[(number >> 48) & 0xff] +
            BitCountTable[(number >> 56) & 0xff];
        }

        /// <summary>
        /// AMD R3 2200G: (1 : 10)
        /// </summary>
        public static void BitCountBenchmark()
        {
            int data = 0x00fff0ff;
            int count = 10000;

            var stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Start();

            for (int i = 0; i < count; i++)
            {
                data.BitCount();
            }

            var t1 = stopwatch.ElapsedTicks;//.ElapsedMilliseconds;
            stopwatch.Restart();

            for (int i = 0; i < count; i++)
            {
                data.BitCount2();
            }

            var t2 = stopwatch.ElapsedTicks;// ElapsedMilliseconds;

            UnityEngine.Debug.Log(t1 + " : " + t2 + ", " + data.BitCount() + ", " + data.BitCount2());
        }

        #endregion

        /*
        public static bool IsBitOn(this int mask, int bit)
        {
            return (mask & bit) != 0;
        }
        */

        #region Bit Index
        public static int Top1Index(this int x)
        {
            if (x == 0) return -1;
            if (x < 0) return 31;

            int n = 30;
            if ((x >> 16) == 0) { n -= 16; x <<= 16; }
            if ((x >> 24) == 0) { n -= 8; x <<= 8; }
            if ((x >> 28) == 0) { n -= 4; x <<= 4; }
            if ((x >> 30) == 0) { n -= 2; x <<= 2; }
            return n - (x >> 31);
        }

        public static int Top1Index(this uint x)
        {
            return Top1Index((int)x);
        }

        public static int Top1Index(this ulong x)
        {
            if (x == 0) return -1;

            int n = 62;
            if ((x >> 32) == 0) { n -= 32; x <<= 32; }
            if ((x >> 48) == 0) { n -= 16; x <<= 16; }
            if ((x >> 56) == 0) { n -= 8; x <<= 8; }
            if ((x >> 60) == 0) { n -= 4; x <<= 4; }
            if ((x >> 62) == 0) { n -= 2; x <<= 2; }

            return n + (int)(x >> 63);
        }

        public static int Low1Index(this int x)
        {
            if (x == 0) return -1;

            int n = 0;
            if ((x << 16) == 0) { n += 16; x >>= 16; }
            if ((x << 24) == 0) { n += 8; x >>= 8; }
            if ((x << 28) == 0) { n += 4; x >>= 4; }
            if ((x << 30) == 0) { n += 2; x >>= 2; }
            if ((x << 31) == 0) { n += 1;}

            return n;
        }

        public static ulong Swap(this ulong bits, int p, int q)
        {
            ulong x = ((bits >> p) ^ (bits >> q)) & 1;
            return bits ^ ((x << p) | (x << q));
        }
        #endregion

        #region BitMask

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool Any(this int value, int mask)
        {
            return (value & mask) != 0;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool All(this int value, int mask)
        {
            return (value & mask) == mask;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool Any(this uint value, uint mask)
        {
            return (value & mask) != 0;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool All(this uint value, uint mask)
        {
            return (value & mask) == mask;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int GetFlag(this int flags, int index)
        {
            return (int)GetFlag((uint)flags, index);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static uint GetFlag(this uint flags, int index)
        {
            return flags >> index & 1u;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int4 GetFlags(this int flags, int4 indices)
        {
            return (int4)GetFlags((uint)flags, indices);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static uint4 GetFlags(this uint flags, int4 indices)
        {
            return new uint4(
                flags >> indices.x,
                flags >> indices.y,
                flags >> indices.z,
                flags >> indices.w
                ) & 1u;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool IsBitOn(this int mask, int index)
        {
            return (mask >> index & 1) != 0;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool IsBitOn(this long mask, int index)
        {
            return (mask >> index & 1) != 0;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool IsBitOn(this ulong mask, int index)
        {
            return (mask >> index & 1) != 0;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void ToBool(this int bits, int shift, out int on, out int off)
        {
            on = (bits >> shift) & 1;
            off = on ^ 1;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Po2Mask(this int number)
        {
            return number - 1;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Po2Shift(this int number)
        {
            return Top1Index(number) + 1;
        }
        #endregion
    }
}